#include <mpi.h>
#include <stdio.h>
#include <unistd.h>

int Q(int x){
    usleep(100000); //simulate time consuming calculations
    return x*2;
}

int main(int argc, char** argv){
    int rank, nproc;
    int TAG = 100;
    MPI_Request send_request, recv_request;
    MPI_Status send_status, recv_status;
    MPI_Init(&argc,&argv);
    MPI_Comm_size(MPI_COMM_WORLD, &nproc);
    MPI_Comm_rank(MPI_COMM_WORLD, &rank);

    int Xbuf[2]={0},Ybuf[2]={0},Xin,Yout;
    int j=0;


    int task_num = 10;
    int task_cnt = 0;
    if (rank==0){
        printf("There is/are %d transferring layer(s)\n", nproc-2);
        int to = rank+1;
        int data;
        while (++task_cnt < task_num){
            data = task_cnt;
            printf("rank=%d\tinput_data=%d\n",rank,data);
            MPI_Isend(&data, 1, MPI_INT, to, TAG, MPI_COMM_WORLD, &send_request);
        }
        data = -1;
        MPI_Isend(&data, 1, MPI_INT, to, TAG, MPI_COMM_WORLD, &send_request); //send end signal
        MPI_Wait(&send_request, &send_status);
        printf("proc#0 send over\n");
    }
    else if (rank == nproc-1){
        int from = rank-1;
        int data=0;
        while (true){
            MPI_Irecv(&data, 1, MPI_INT, from, TAG, MPI_COMM_WORLD, &recv_request);
            MPI_Wait(&recv_request, &recv_status);
            if (data == -1){
                printf("proc#%d print result over\n",rank);
                break;
            }
            if (data!=0){
                printf("rank=%d\toutput_data=%d\n",rank, data);
            }
        }
    }
    else{
        int from = rank-1;
        int to = rank+1;
        int data=0;
        while (true){
            j = 1-j;
            MPI_Isend(Ybuf+(1-j), 1, MPI_INT, to, TAG, MPI_COMM_WORLD, &send_request);
            if (Xbuf[j]==-1){
                MPI_Isend(Xbuf+j, 1, MPI_INT, to, TAG, MPI_COMM_WORLD, &send_request);
                MPI_Wait(&send_request, &send_status);
                printf("proc#%d transfer over\n",rank);
                break;
            }
            MPI_Irecv(Xbuf+(1-j), 1, MPI_INT, from, TAG, MPI_COMM_WORLD, &recv_request);
            Ybuf[j] = Q(Xbuf[j]);
            MPI_Wait(&recv_request, &recv_status);
            MPI_Wait(&send_request, &send_status);
        }
    }
    MPI_Finalize();
    


    return 0;
}